using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Configuration;
using System.Linq;
using System.Drawing;
using System.Runtime.Serialization;
using System.Threading;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Runtime;
using TransTaskWithCompensation.BankServiceReference;
using TransTaskWithCompensation.OrderServiceReference;

namespace TransTaskWithCompensation
{
    [PersistOnClose]
    public sealed partial class WorkflowMain : SequentialWorkflowActivity
    {
        [NonSerialized]
        private OrderContractClient orderServiceProxy;

        [NonSerialized]
        private BankContractClient bankServiceProxy;

        public WorkflowMain()
        {
            InitializeComponent();
        }

        #region Member

        [DataMember]
        private Order orderItem
        {
            get;
            set;
        }

        private int retryTimes
        {
            get;
            set;
        }

        #endregion


        private void CreatedExecuteCode(object sender, EventArgs e)
        {
            LogHelper.LogMessage("Initialized Activity..." + WorkflowEnvironment.WorkflowInstanceId.ToString());
        }

        private bool CheckOrderServiceProxy(bool defaultValue)
        {
            bool result = defaultValue;
            try
            {
                LogHelper.LogMessage("Try to connect to order service...");
                if (orderServiceProxy == null)
                {
                    orderServiceProxy = new OrderContractClient();
                }
                result = orderServiceProxy.IsAvailable();
                if (result)
                {
                    LogHelper.LogMessage("Order Service is available.");
                }
            }
            catch (Exception ex)
            {
                LogHelper.LogException(ex);

                LogHelper.LogMessage("Order Service is unavailable.");
            }

            return result;
        }

        private void OrderServiceCheckHandler(object sender, ConditionalEventArgs e)
        {
            e.Result = CheckOrderServiceProxy(false);
        }

        private void GotOrderToProcess_ExecuteCode(object sender, EventArgs e)
        {
            if (orderItem == null)
            {
                try
                {
                    orderItem = orderServiceProxy.GetOrder();

                    string msg = string.Format("Got order to process: #{0}, Amount: {1:F2}",
                        orderItem.OrderID,
                        orderItem.Amount
                        );

                    LogHelper.LogMessage(msg);
                }
                catch (Exception ex)
                {
                    LogHelper.LogException(ex);
                }
            }
        }

        private void BankServiceCheckHandler(object sender, ConditionalEventArgs e)
        {
            e.Result = true;
            try
            {
                LogHelper.LogMessage("Try to connect to bank service...");
                bankServiceProxy = new BankContractClient();
                e.Result = !bankServiceProxy.IsAvailable();
                if (e.Result)
                {
                    LogHelper.LogMessage("Bank Service is available.");
                }
            }
            catch (Exception ex)
            {
                LogHelper.LogException(ex);
                LogHelper.LogMessage("Bank Service is unavailable.");
            }
        }

        private void ChargeStateCheckHandler(object sender, ConditionalEventArgs e)
        {
            e.Result = false;

            if (retryTimes >= 3)
            {
                e.Result = false;
            }
            else
            {
                e.Result = orderItem != null && !orderItem.Processed;
            }
        }

        private void WaitForRetry()
        {
            if (++retryTimes > 1)
            {
                LogHelper.LogMessage("wait for 10 seconds to retry charge money to process order: #" + orderItem.OrderID);
                Thread.Sleep(10 * 1000);
            }
        }

        private void RequestPending_ExecuteCode(object sender, EventArgs e)
        {
            WaitForRetry();

            if (orderItem != null && !orderItem.Processed)
            {
                LogHelper.LogMessage("Request charge money to process order: #" + orderItem.OrderID);

                foreach(Payment item in orderItem.Payments)
                {
                    item.Processed = bankServiceProxy.Pending(item.CardNumber, orderItem.Amount);
                    orderServiceProxy.SetPayment(
                        orderItem, 
                        item.TransactionId, 
                        item.Processed );
                }

                orderItem.Processed
                    = orderItem.Payments.Where(item => !item.Processed).Count() == 0;

                if (orderItem.Processed)
                {
                    LogHelper.LogMessage("Succeed Pending amount: " + orderItem.Amount.ToString("F2"), ConsoleColor.Green);
                }
                else
                {
                    LogHelper.LogMessage("!Failed to pending amount: " + orderItem.Amount.ToString("F2"), ConsoleColor.Red);
                }
            }
        }

        private void OrderPaymentCheckHandler(object sender, ConditionalEventArgs e)
        {
            e.Result = orderItem != null && orderItem.Processed;
        }

        private void ProductCheckHandler(object sender, ConditionalEventArgs e)
        {
            if (orderItem == null)
            {
                e.Result = false;
            }
            else
            {
                e.Result = orderServiceProxy.CheckProductIsAvailable(orderItem.OrderID);

                LogHelper.LogMessage("Check Product Items are Available for process order: #"
                    + orderItem.OrderID.ToString()
                    + ", Result:" + (e.Result ? "Succeed" : "Failed"),
                    (e.Result ? ConsoleColor.Green : ConsoleColor.Red)
                    );

            }
        }

        private void ShipProduct_ExecuteCode(object sender, EventArgs e)
        {
            if (orderItem != null)
            {
                orderServiceProxy.ShipOrder(orderItem.OrderID);
                LogHelper.LogMessage("Order shipped at: " + DateTime.Now.ToLongDateString());
            }
        }

        private void CommitPending_ExecuteCode(object sender, EventArgs e)
        {
            if (orderItem != null)
            {
                foreach (Payment item in orderItem.Payments)
                {
                    bankServiceProxy.CommitPending(item.CardNumber, item.Amount);
                }
                
                LogHelper.LogMessage("Commit Pending Amount: " + orderItem.Amount.ToString("F2"), ConsoleColor.Green);
            }
        }

        private void CancelPending_ExecuteCode(object sender, EventArgs e)
        {
            if (orderItem != null && orderItem.Processed)
            {
                try
                {
                    if (bankServiceProxy == null)
                    {
                        bankServiceProxy = new BankContractClient();
                    }

                    foreach (Payment item in orderItem.Payments)
                    {
                        bankServiceProxy.CancelPending(item.CardNumber, item.Amount);
                    }
                    
                }
                catch(Exception ex)
                {
                    throw new DiscontinuedProductException(ex.Message);
                }

                LogHelper.LogMessage("Cancel Pending Amount: " + orderItem.Amount.ToString("F2"), ConsoleColor.Yellow);
            }
        }

        private void CheckOrderService_ExecuteCode(object sender, ConditionalEventArgs e)
        {
            e.Result = !CheckOrderServiceProxy(true);
        }

        private void ProductDiscontinued_ExecuteCode(object sender, EventArgs e)
        {
            if (orderItem != null)
            {
                LogHelper.LogMessage("Cancel Order: #" + orderItem.OrderID, ConsoleColor.Red);
                orderServiceProxy.CancelOrder(orderItem.OrderID);
            }

            LogHelper.LogMessage("Product ordered has been discontinued", ConsoleColor.Red);

            SendFeedBackMail(sender, e);
        }

        private void SendFeedBackMail(object sender, EventArgs e)
        {
            if (orderItem != null)
            {
                if (orderItem.Processed)
                {
                    LogHelper.LogMessage("Send Customer Email,OrderItem: #" + orderItem.OrderID + " Product is unavailable!\r\n", ConsoleColor.Magenta);
                }
                else
                {
                    LogHelper.LogMessage("Send Customer Email,OrderItem: #" + orderItem.OrderID + " Charge from account failed!\r\n", ConsoleColor.Magenta);
                }
            }
        }

        private void CompletedActivity_ExecuteCode(object sender, EventArgs e)
        {
            if (orderItem == null)
            {
                LogHelper.LogMessage("Abort Process.", ConsoleColor.Red);
            }
            else
            {
                LogHelper.LogMessage("Completed Process.");
            }
        }

        private void CancelledActivity_ExecuteCode(object sender, EventArgs e)
        {
            LogHelper.LogMessage("Cancelled Activity...");
        }

        public static DependencyProperty DiscontinuedWorkFlowProcessHandlerProperty = DependencyProperty.Register("DiscontinuedWorkFlowProcessHandler", typeof(System.Exception), typeof(WorkflowMain));

        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        [BrowsableAttribute(true)]
        [CategoryAttribute("Handlers")]
        public DiscontinuedProductException DiscontinuedWorkFlowProcessHandler
        {
            get
            {
                return ((DiscontinuedProductException)(base.GetValue(WorkflowMain.DiscontinuedWorkFlowProcessHandlerProperty)));
            }
            set
            {
                base.SetValue(WorkflowMain.DiscontinuedWorkFlowProcessHandlerProperty, value);
            }
        }

    }

}
